package
{
	import flash.utils.ByteArray;
	
	/*	
	READ ASAP:
	http://www.unitzeroone.com/blog/2009/05/22/another-scream-on-flash-alchemy-memory-and-compilers/
	http://llvm.org/devmtg/2008-08/Petersen_FlashCCompiler.pdf
	
	http://labs.adobe.com/downloads/alchemy.html
	*/
	
	
	public class RAM
	{
		/* should memory grow? */
		// public static const InitRAM:int = 1024; /* bytes! */
		/* do video acceleration transparently - inside the sim, blit looks same as memcpy, etc. */
		public static var bytes:ByteArray = new ByteArray(); /* the "physical memory": byte-addressable */
		/* video is the only memory-mapped device */
		
		/* forget this crap - let's check if bit 31 is set, then -> vRAM */
		// public static var vRAMBottom:uint = 1000;
		// public static var vRAMTop:uint = vRAMBottom + (Console.TvH*Console.TvY); /* bounds of vRAM - let's put it on top of RAM */
		// if (source >= vRAMBottom && source <= vRAMTop) ...
		public static const vRAMbottom:uint = 0x80000000; /* replace 'vRAMbottom' check with >> 31 for speed? */
		
		public static function InitRAM():void {
			for (var i:uint = 0; i<(1024*768*1); i++) RAM.bytes[i] = 0;
		}
		
		/*
		MemCopy Cases:
			RAM->RAM
			Video->Video
			RAM->Video
			Video->RAM
			Mixed
		*/
		
		// flash 10?
		//var vector:Vector.<int> = new Vector.<int>();
		
		/******* MemCopy *******/
		
		/* we need to adjust vbottom and vtop to stay clear of word boundaries */
		
		/* Sad, slow version. We can make a non-stupid one later. */
		public static function MemCopy(source:uint, dest:uint, count:uint):void {
			debug("MemCopy: source="+source+" dest="+dest+" count="+count);
			/* TODO: check for src/dest overlap: an error */
			/* TODO: check for boundary crossing errors */
			if (source & vRAMbottom) { /* Source is in vRAM */
				//source &= (~vRAMbottom);
				if (dest & vRAMbottom) { /* Destination is in vRAM */
					//dest &= (~vRAMbottom);
					debug("MemCopy: VRAM to VRAM");
					SlowMemCopy(source, dest, count);
					//Console.TvRAM.copyPixels(
					debug("MemCopy: OK");
				} else { /* Destination is NOT in vRAM */
					debug("MemCopy: VRAM to RAM");
					debug("source="+source+" dest="+dest+" count="+count);
					SlowMemCopy(source, dest, count);
					debug("MemCopy: OK");
					//RAM.bytes.writeBytes(Console.TvRAM.getPixels(new Rectangle(?,?,?,?)));
				}
			} else { /* Source is NOT in vRAM */
				if (dest & vRAMbottom) { /* Destination is in vRAM */
					debug("MemCopy: RAM to VRAM");
					//dest &= (~vRAMbottom);
					SlowMemCopy(source, dest, count);
					debug("MemCopy: OK");
				} else { /* Destination is NOT in vRAM */
					debug("MemCopy: RAM to RAM");
					RAM.bytes.position = dest;
					RAM.bytes.readBytes(RAM.bytes, source, count);
					debug("MemCopy: OK");
				}
			}
		}
		
		public static function SlowMemCopy(source:uint, dest:uint, count:uint):void {
			debug("SlowMemCopy: source="+source+" dest="+dest+" count="+count);
			for (var i:uint = 0; i<count; i++) WriteW(dest, ReadW(source));
		}
		
		
		/******* PEEK/POKE *******/		
		/* possibly this should all be cut and moved to the CPU core */
		
		/* Load a single word from memory. */
		public static function ReadW(addr:uint):uint {
			if (addr & vRAMbottom) {
				//if (addr & 3) throw new Error("Unaligned video memory access!");
				addr &= (~vRAMbottom);
				//return Console.TvRAM.getPixel(addr%Console.TvH, addr/Console.TvH);
				return Console.TvShadow[addr];
			} else {
				RAM.bytes.position = addr;
				return RAM.bytes.readUnsignedInt();
			}
		}

		/* Save a single word to memory. */
		public static function WriteW(addr:uint, val:uint):void {
			if (addr & vRAMbottom) {
				//if (addr & 3) throw new Error("Unaligned video memory access!");
				addr &= (~vRAMbottom);
				//Console.TvRAM.setPixel(addr%Console.TvH, addr/Console.TvH, val);
				Console.TvShadow[addr] = val;
			} else {
				RAM.bytes.position = addr;
				RAM.bytes.writeUnsignedInt(val);
			}
		}
		
		/************************/
		
        private static function debug(msg:String):void {
        	m1.debug.print("RAM: " + msg);
        }
	}
}